package com.zhiwei.most.views

import android.text.TextPaint
import android.animation.ValueAnimator
import com.blankj.utilcode.util.SizeUtils
import com.zhiwei.most.R
import android.content.res.Resources.NotFoundException
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import androidx.annotation.ColorLong
import com.blankj.utilcode.util.ColorUtils
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * Create by zhiweizhu on 2022/3/3
 * 圆形进度条
 */
class CircleProgressBar @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val ANTI_ALIAS = true
    private val DEFAULT_SIZE = 150
    private val DEFAULT_START_ANGLE = 270
    private val DEFAULT_SWEEP_ANGLE = 360
    private val DEFAULT_ANIM_TIME = 1000
    private val DEFAULT_MAX_VALUE = 100
    private val DEFAULT_VALUE = 0
    private val DEFAULT_HINT_SIZE = 15
    private val DEFAULT_UNIT_SIZE = 30
    private val DEFAULT_VALUE_SIZE = 15
    private val DEFAULT_ARC_WIDTH = 15
    private val DEFAULT_WAVE_HEIGHT = 40

    //默认大小
    private var mDefaultSize = 0

    //是否开启抗锯齿
    var isAntiAlias = false
        private set

    //绘制提示
    private var mHintPaint: TextPaint? = null
    var hint: CharSequence? = null
    private var mHintColor = 0
    private var mHintSize = 0f
    private var mHintOffset = 0f

    //绘制单位
    private var mUnitPaint: TextPaint? = null
    var unit: CharSequence? = null
    private var mUnitColor = 0
    private var mUnitSize = 0f
    private var mUnitOffset = 0f

    //绘制数值
    private var mValuePaint: TextPaint? = null
    private var mValue = 0f
    /**
     * 获取最大值
     *
     * @return
     */
    /**
     * 设置最大值
     */
    var maxValue = 0f
    private var mValueOffset = 0f
    private var mPrecision = 0
    private var mPrecisionFormat: String? = null
    private var mValueColor = 0
    private var mValueSize = 0f

    //绘制圆弧
    private var mArcPaint: Paint? = null
    private var mArcWidth = 0f
    private var mStartAngle = 0f
    private var mSweepAngle = 0f
    private var mRectF: RectF? = null

    //渐变的颜色是360度，如果只显示270，那么则会缺失部分颜色
    private var mSweepGradient: SweepGradient? = null

    @ColorLong
    private var mGradientColors = intArrayOf(Color.GREEN, Color.YELLOW, Color.RED)

    //当前进度，[0.0f,1.0f]
    private var mPercent = 0f

    //动画时间
    var animTime: Long = 0

    //属性动画
    private var mAnimator: ValueAnimator? = null

    //绘制背景圆弧
    private var mBgArcPaint: Paint? = null
    private var mBgArcColor = 0
    private var mBgArcWidth = 0f

    //圆心坐标，半径
    private var mCenterPoint: Point? = null
    private var mRadius = 0f
    private var mTextOffsetPercentInRadius = 0f
    private fun init(attrs: AttributeSet?) {
        mDefaultSize = SizeUtils.dp2px(DEFAULT_SIZE.toFloat())
        mAnimator = ValueAnimator()
        mRectF = RectF()
        mCenterPoint = Point()
        initAttrs(attrs)
        initPaint()
        value = mValue
    }

    private fun initAttrs(attrs: AttributeSet?) {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar)
        isAntiAlias = typedArray.getBoolean(R.styleable.CircleProgressBar_antiAlias, ANTI_ALIAS)
        hint = typedArray.getString(R.styleable.CircleProgressBar_hint)
        mHintColor = typedArray.getColor(R.styleable.CircleProgressBar_hintColor, Color.BLACK)
        mHintSize = typedArray.getDimension(
            R.styleable.CircleProgressBar_hintSize,
            DEFAULT_HINT_SIZE.toFloat()
        )
        mValue = typedArray.getFloat(R.styleable.CircleProgressBar_value, DEFAULT_VALUE.toFloat())
        maxValue =
            typedArray.getFloat(R.styleable.CircleProgressBar_maxValue, DEFAULT_MAX_VALUE.toFloat())
        //内容数值精度格式
        mPrecision = typedArray.getInt(R.styleable.CircleProgressBar_precision, 0)
        mPrecisionFormat = getPrecisionFormat(mPrecision)
        mValueColor = typedArray.getColor(R.styleable.CircleProgressBar_valueColor, Color.BLACK)
        mValueSize = typedArray.getDimension(
            R.styleable.CircleProgressBar_valueSize,
            DEFAULT_VALUE_SIZE.toFloat()
        )
        unit = typedArray.getString(R.styleable.CircleProgressBar_unit)
        mUnitColor = typedArray.getColor(R.styleable.CircleProgressBar_unitColor, Color.BLACK)
        mUnitSize = typedArray.getDimension(
            R.styleable.CircleProgressBar_unitSize,
            DEFAULT_UNIT_SIZE.toFloat()
        )
        mArcWidth = typedArray.getDimension(
            R.styleable.CircleProgressBar_arcWidth,
            DEFAULT_ARC_WIDTH.toFloat()
        )
        mStartAngle = typedArray.getFloat(
            R.styleable.CircleProgressBar_startAngle,
            DEFAULT_START_ANGLE.toFloat()
        )
        mSweepAngle = typedArray.getFloat(
            R.styleable.CircleProgressBar_sweepAngle,
            DEFAULT_SWEEP_ANGLE.toFloat()
        )
        mBgArcColor = typedArray.getColor(R.styleable.CircleProgressBar_bgArcColor, Color.WHITE)
        mBgArcWidth = typedArray.getDimension(
            R.styleable.CircleProgressBar_bgArcWidth,
            DEFAULT_ARC_WIDTH.toFloat()
        )
        mTextOffsetPercentInRadius =
            typedArray.getFloat(R.styleable.CircleProgressBar_textOffsetPercentInRadius, 0.33f)
        mPercent = typedArray.getFloat(R.styleable.CircleProgressBar_percent, 0f)
        animTime =
            typedArray.getInt(R.styleable.CircleProgressBar_animTime, DEFAULT_ANIM_TIME).toLong()
        val gradientArcColors = typedArray.getResourceId(R.styleable.CircleProgressBar_arcColors, 0)
        if (gradientArcColors != 0) {
            try {
                val gradientColors = resources.getIntArray(gradientArcColors)
                when {
                    gradientColors.isEmpty() -> { //如果渐变色为数组为0，则尝试以单色读取色值
                        val color = ColorUtils.getColor(gradientArcColors)
                        mGradientColors = IntArray(2)
                        mGradientColors[0] = color
                        mGradientColors[1] = color
                    }
                    gradientColors.size == 1 -> { //如果渐变数组只有一种颜色，默认设为两种相同颜色
                        mGradientColors = IntArray(2)
                        mGradientColors[0] = gradientColors[0]
                        mGradientColors[1] = gradientColors[0]
                    }
                    else -> {
                        mGradientColors = gradientColors
                    }
                }
            } catch (e: NotFoundException) {
                throw NotFoundException("the give resource not found.")
            }
        }
        typedArray.recycle()
    }

    private fun getPrecisionFormat(precision: Int): String {
        return "%." + precision + "f"
    }

    private fun initPaint() {
        mHintPaint = TextPaint().apply {
            isAntiAlias = this@CircleProgressBar.isAntiAlias  //设置抗锯齿,会消耗较大资源，绘制图形速度会变慢。
            textSize = mHintSize   // 设置绘制文字大小
            // 设置画笔颜色
            color = mHintColor
            // 从中间向两边绘制，不需要再次计算文字
            textAlign = Paint.Align.CENTER

        }
        mValuePaint = TextPaint().apply {
            // 设置Typeface对象，即字体风格，包括粗体，斜体以及衬线体，非衬线体等
            isAntiAlias = this@CircleProgressBar.isAntiAlias
            textSize = mValueSize
            color = mValueColor
            typeface = Typeface.DEFAULT_BOLD
            textAlign = Paint.Align.CENTER
        }

        mUnitPaint = TextPaint().apply {
            isAntiAlias = this@CircleProgressBar.isAntiAlias
            textSize = mUnitSize
            color = mUnitColor
            textAlign = Paint.Align.CENTER
        }
        mArcPaint = Paint().apply {
            isAntiAlias = this@CircleProgressBar.isAntiAlias
            // 设置画笔的样式，为FILL，FILL_OR_STROKE，或STROKE
            style = Paint.Style.STROKE
            // 设置画笔粗细
            strokeWidth = mArcWidth
            // 当画笔样式为STROKE或FILL_OR_STROKE时，设置笔刷的图形样式，如圆形样式
            // Cap.ROUND,或方形样式 Cap.SQUARE
            strokeCap = Paint.Cap.ROUND
        }
        mBgArcPaint = Paint().apply {
            isAntiAlias = this@CircleProgressBar.isAntiAlias
            color = mBgArcColor
            style = Paint.Style.STROKE
            strokeWidth = mBgArcWidth
            strokeCap = Paint.Cap.ROUND
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(
            measureView(widthMeasureSpec, mDefaultSize),
            measureView(heightMeasureSpec, mDefaultSize)
        )
    }

    private fun measureView(measureSpec: Int, defaultSize: Int): Int {
        var result = defaultSize
        val specMode = MeasureSpec.getMode(measureSpec)
        val specSize = MeasureSpec.getSize(measureSpec)
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize
        } else if (specMode == MeasureSpec.AT_MOST) {
            result = min(result, specSize)
        }
        return result
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        //求圆弧和背景圆弧的最大宽度
        val maxArcWidth = max(mArcWidth, mBgArcWidth)
        //求最小值作为实际值
        val minSize = min(
            w - paddingLeft - paddingRight - 2 * maxArcWidth.toInt(),
            h - paddingTop - paddingBottom - 2 * maxArcWidth.toInt()
        )
        //减去圆弧的宽度，否则会造成部分圆弧绘制在外围
        mRadius = (minSize / 2).toFloat()
        //获取圆的相关参数
        mCenterPoint!!.x = w / 2
        mCenterPoint!!.y = h / 2
        //绘制圆弧的边界
        mRectF!!.left = mCenterPoint!!.x - mRadius - maxArcWidth / 2
        mRectF!!.top = mCenterPoint!!.y - mRadius - maxArcWidth / 2
        mRectF!!.right = mCenterPoint!!.x + mRadius + maxArcWidth / 2
        mRectF!!.bottom = mCenterPoint!!.y + mRadius + maxArcWidth / 2
        //计算文字绘制时的 baseline
        //由于文字的baseline、descent、ascent等属性只与textSize和typeface有关，所以此时可以直接计算
        //若value、hint、unit由同一个画笔绘制或者需要动态设置文字的大小，则需要在每次更新后再次计算
        mValueOffset = mCenterPoint!!.y + getBaselineOffsetFromY(mValuePaint)
        mHintOffset =
            mCenterPoint!!.y - mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(
                mHintPaint
            )
        mUnitOffset =
            mCenterPoint!!.y + mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(
                mUnitPaint
            )
        updateArcPaint()
    }

    private fun getBaselineOffsetFromY(paint: Paint?): Float {
        return measureTextHeight(paint) / 2
    }

    private fun measureTextHeight(paint: Paint?): Float {
        val fontMetrics = paint!!.fontMetrics
        return abs(fontMetrics.ascent) - fontMetrics.descent
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawText(canvas)
        drawArc(canvas)
    }

    /**
     * 绘制内容文字
     *
     * @param canvas
     */
    private fun drawText(canvas: Canvas) {
        // 计算文字宽度，由于Paint已设置为居中绘制，故此处不需要重新计算
        // float textWidth = mValuePaint.measureText(mValue.toString());
        // float x = mCenterPoint.x - textWidth / 2;
        if (hint != null) {
            canvas.drawText(hint.toString(), mCenterPoint!!.x.toFloat(), mHintOffset, mHintPaint!!)
        }
        if (unit != null) {
            canvas.drawText(unit.toString(), mCenterPoint!!.x.toFloat(), mUnitOffset, mUnitPaint!!)
            canvas.drawText(
                String.format(mPrecisionFormat!!, mValue),
                mCenterPoint!!.x.toFloat(),
                mValueOffset,
                mValuePaint!!
            )
        }
    }

    private fun drawArc(canvas: Canvas) {
        // 绘制背景圆弧
        // 从进度圆弧结束的地方开始重新绘制，优化性能
        canvas.save()
        val currentAngle = mSweepAngle * mPercent
        canvas.rotate(mStartAngle, mCenterPoint!!.x.toFloat(), mCenterPoint!!.y.toFloat())
        canvas.drawArc(mRectF!!, currentAngle, mSweepAngle - currentAngle + 2, false, mBgArcPaint!!)
        // 第一个参数 oval 为 RectF 类型，即圆弧显示区域
        // startAngle 和 sweepAngle  均为 float 类型，分别表示圆弧起始角度和圆弧度数
        // 3点钟方向为0度，顺时针递增
        // 如果 startAngle < 0 或者 > 360,则相当于 startAngle % 360
        // useCenter:如果为True时，在绘制圆弧时将圆心包括在内，通常用来绘制扇形
        canvas.drawArc(mRectF!!, 2f, currentAngle, false, mArcPaint!!)
        canvas.restore()
    }

    /**
     * 更新圆弧画笔
     */
    private fun updateArcPaint() {
        // 设置渐变
        mSweepGradient = SweepGradient(
            mCenterPoint!!.x.toFloat(),
            mCenterPoint!!.y.toFloat(),
            mGradientColors,
            null
        )
        mArcPaint!!.shader = mSweepGradient
    }

    /**
     * 设置当前值
     */
    var value: Float
        get() = mValue
        set(value) {
            var valueTemp = value
            if (valueTemp > maxValue) {
                valueTemp = maxValue
            } else if (valueTemp < 0) {
                valueTemp = 0f
            }
            val start = mPercent
            val end = valueTemp / maxValue
            startAnimator(start, end, animTime)
        }

    private fun startAnimator(start: Float, end: Float, animTime: Long) {
        mAnimator = ValueAnimator.ofFloat(start, end)
        mAnimator?.let {
            it.duration = animTime
            it.addUpdateListener { animation ->
                mPercent = animation.animatedValue as Float
                mValue = mPercent * maxValue
                invalidate()
            }
            it.start()
        }

    }

    /**
     * 获取精度
     * @return
     */
    var precision: Int
        get() = mPrecision
        set(precision) {
            mPrecision = precision
            mPrecisionFormat = getPrecisionFormat(precision)
        }

    /**
     * 设置渐变
     *
     * @param gradientColors
     */
    var gradientColors: IntArray
        get() = mGradientColors
        set(gradientColors) {
            mGradientColors = gradientColors
            updateArcPaint()
        }

    /**
     * 重置
     */
    fun reset() {
        startAnimator(mPercent, 0.0f, 1000L)
    }

    init {
        init(attrs)
    }
}